import { TrackOperationType, TriggerOperationType } from "./operation.js"

const targetMap = new WeakMap()
const ITERATOR_KEY = Symbol('iterator')
// 反正函数中嵌套再effect
const effectStack = []
let activeEffect = null

let shouldTrack = true

export function pauseTracking() {
  shouldTrack = false
}

export function resumeTracking() {
  shouldTrack = true
}

export function effect(fn, options = {}) {
  const { lazy = false } = options
  const effectFn = () => {
    try {
      // 每次执行前，清空所有的依赖项
      cleanup(effectFn)
      // 下次重新运行的不是fn，而是整个effect环境
      activeEffect = effectFn
      effectStack.push(effectFn)
      // 这里在调用完函数后，可能会走到依赖收集，依赖收集中正好可以访问到activeEffect函数
      return fn()
    } finally {
      effectStack.pop()
      activeEffect = effectStack[effectStack.length - 1]
    }
  }
  effectFn.deps = []
  effectFn.options = options
  if (!lazy) {
    effectFn()
  }
  return effectFn
}

export function cleanup(effectFn) {
  const { deps } = effectFn
  if (!deps.length) return
  for (const dep of deps) {
    dep.delete(effectFn)
  }
  deps.length = 0
}

/** 依赖收集 */
export function track(target, type, key) {
  if (!shouldTrack || !activeEffect) {
    return
  }
  let keyMap = targetMap.get(target)
  if (!keyMap) {
    keyMap = new Map()
    targetMap.set(target, keyMap)
  }
  if (type === TrackOperationType.INERATOR) {
    key = ITERATOR_KEY
  }
  let typeMap = keyMap.get(key)
  if (!typeMap) {
    typeMap = new Map()
    keyMap.set(key, typeMap)
  }
  let depSet = typeMap.get(type)
  if (!depSet) {
    depSet = new Set()
    typeMap.set(type, depSet)
  }
  if (!depSet.has(activeEffect)) {
    depSet.add(activeEffect)
    activeEffect.deps.push(depSet)
  }
  console.log('%c收集', 'color: red;', targetMap)
}

/** 派发更新 */
export function trigger(target, type, key) {
  const effectFns = getEffectFns(target, type, key)
  if (!effectFns) return
  for (const effectFn of effectFns) {
    // 在收集依赖时，不派发更新
    if (effectFn === activeEffect) {
      continue
    }
    const { scheduler } = effectFn.options
    if (scheduler) {
      scheduler()
    } else {
      effectFn()
    }
    console.log('%c派发', 'color: blue;', `${type}-${key}`)
  }
}

function getEffectFns(target, type, key) {
  const keyMap = targetMap.get(target)
  if (!keyMap) return

  const keys = [key]
  // 添加或删除影响迭代
  if (type === TriggerOperationType.ADD || type === TriggerOperationType.DELETE) {
    keys.push(ITERATOR_KEY)
  }
  const effectFns = new Set()
  const triggerTypeMap = {
    [TriggerOperationType.SET]: [TrackOperationType.GET],
    [TriggerOperationType.ADD]: [TrackOperationType.GET, TrackOperationType.HAS, TrackOperationType.INERATOR],
    [TriggerOperationType.DELETE]: [TrackOperationType.GET, TrackOperationType.HAS, TrackOperationType.INERATOR],
  }
  for (const key of keys) {
    const typeMap = keyMap.get(key)
    if (!typeMap) {
      continue
    }
    const trackTypes = triggerTypeMap[type]
    for (const trackType of trackTypes) {
      const depSet = typeMap.get(trackType)
      if (!depSet) {
        continue
      }
      for (const effectFn of depSet) {
        effectFns.add(effectFn)
      }
    }
  }

  return effectFns
}